package org.broadway.monitor.dsl;

import org.apache.commons.lang.StringUtils;
import org.broadway.interfaces.Action;
import org.broadway.interfaces.ObservedResource;
import org.broadway.monitor.BeanMonitor;
import org.broadway.monitor.Monitor;
import org.broadway.monitor.MonitorConstants;
import org.broadway.monitor.ObservedBean;
import org.broadway.monitor.MappedResourceCollector;

/**
 * The BeanMonitorDsl offers a domain specific languange to
 * construct and execute mononitoring of java beans.
 * It accepts an existing monitor object.  If none is provided,
 * it will error out.
 * <p>
 * The expected usage for the dsl is:
 * <code>
 * MonitorDsl mon = new MonitorDsl(Monitor);
 * mon
 * 	.observe(new ObservedBean("bean1", myBean1))
 * 	.observe(new ObservedBean("bean2", myBean2))
 * 	.observe(new ObservedBean("bean3", myBean3))
 * 	.when(When.isTrueFor("${exp}").execute(action))
 *  .scan();
 * </code>
 * </p>
 * @author vladimir vivien
 * @see ObservedBean
 * @see Conditional
 *
 */
public class BeanMonitorDsl implements MonitorDsl {
	private BeanMonitor monitor;

	/**
	 * Constructor.  You must provide the Monitor object
	 * which will be used for the dsl statements.
	 * @param mon
	 */
	public BeanMonitorDsl(Monitor mon) {
		if(mon == null){
			throw new IllegalArgumentException(
					"Requires a valid, fully-realized Monitor object.");
		}
		monitor = (BeanMonitor)mon;
	}

	/**
	 * Use this method to add a resource which is being monitored.
	 * The ObservedResource is a wrapper class which captures an id/alias
	 * and the bean being observed.  The alias is used to refer to the bean
	 * in textual expressions.
	 */
	public MonitorDsl observe(ObservedResource res) {
		if(res == null)
			throw new IllegalArgumentException("Unable to observe a null object.");

		ObservedBean bean = (ObservedBean)res;
		if(StringUtils.isBlank(bean.getId()))
			throw new IllegalArgumentException("An alias/id must be provided for observed object.");

		getInternalMonitorResCollector().putResource(bean.getId(), bean.getValue());
		return this;
	}

	/**
	 * This method is a shortcut of the implemented {@link #observe(ObservedResource)} method.
	 * @param alias - alias name used to refer to the bean being observed.
	 * @param bean - the actual being being observed.
	 * @return MonitorDsl
	 */
	public MonitorDsl observe(String alias, Object bean){
		if(StringUtils.isBlank(alias))
			throw new IllegalArgumentException("An alias/id must be provided for observed object.");
		getInternalMonitorResCollector().putResource(alias, bean);
		return this;
	}

	/**
	 * This implementation method accepts a Conditional object.
	 * The Conditional object captures an conditional expression
	 * that will be evaluated by the monitor. When an expecte value
	 * is evaluated (true/false), an action will be fired by the monitor.
	 * @param Conditional
	 * @return MonitorDsl
	 * @see Conditional
	 */
	public MonitorDsl forCondition(Conditional condition) {
		if(condition == null)
			throw new IllegalArgumentException("A conditional object is required for evaluation.");
		BinaryCondition cond = (BinaryCondition) condition;
		monitor.setMonitorExpression(cond.getExpression());
		monitor.setActWhen(cond.getExecWhen());
		return this;
	}

	/***
	 * This is a stopping method for this dsl.  When it's called,
	 * we are assuming that the necessary statements have been made
	 * to express how to run the monitor.  It will scan all registered
	 * observed objects and evaluates the monitoring expression for
	 * a certain condition.
	 */
	public Object execute(Action action) {
		monitor.setAction(action);
		monitor.scan();
		return monitor.getContext().getValue(MonitorConstants.MonitorKeys.ACTION_RESULT.toString());
	}

	// ******************** Private Methods ******************** //
	private MappedResourceCollector getInternalMonitorResCollector() {
		return (MappedResourceCollector) monitor.getResourceCollector();
	}

}
